package com.dapeng.cleardatabackend.interceptor.aop;

import com.dapeng.cleardatabackend.annotation.MultiTransactional;
import com.dapeng.cleardatabackend.common.ErrorCode;
import com.dapeng.cleardatabackend.exception.BusinessException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import java.util.Stack;

/**
 * 自定义多事务管理
 *
 * @author dapeng
 */
@Aspect
@Component
public class TransactionAspect {

    @Resource
    private PlatformTransactionManager mysqlTransactionManager;

    @Resource
    private PlatformTransactionManager neo4jTransactionManager;

    /**
     * 多事务
     *
     * @param proceedingJoinPoint 连接点
     * @param multiTransactional  多事务
     * @return {@link Object }
     */
    @Around("@annotation(multiTransactional)")
    public Object multiTransactional(ProceedingJoinPoint proceedingJoinPoint, MultiTransactional multiTransactional) {
        Propagation propagation = multiTransactional.propagation();
        // 先开启事务的最后提交
        Stack<PlatformTransactionManager> dtmStack = new Stack<>();
        Stack<TransactionStatus> tsStack = new Stack<>();
        // 先开启mysql
        DefaultTransactionDefinition mysqlTxDefinition = new DefaultTransactionDefinition();
        mysqlTxDefinition.setPropagationBehavior(propagation.value());
        TransactionStatus mysqlTransactionStatus = mysqlTransactionManager.getTransaction(mysqlTxDefinition);
        dtmStack.push(mysqlTransactionManager);
        tsStack.push(mysqlTransactionStatus);

        // 再开启neo4j
        DefaultTransactionDefinition neo4jTxDefinition = new DefaultTransactionDefinition();
        neo4jTxDefinition.setPropagationBehavior(propagation.value());
        TransactionStatus neo4jTransactionStatus = neo4jTransactionManager.getTransaction(neo4jTxDefinition);
        dtmStack.push(neo4jTransactionManager);
        tsStack.push(neo4jTransactionStatus);

        try {
            Object obj = proceedingJoinPoint.proceed();
            while (!dtmStack.isEmpty()) {
                TransactionStatus transactionStatus = tsStack.pop();
                PlatformTransactionManager transactionManager = dtmStack.pop();
                if (!transactionStatus.isCompleted()) {
                    transactionManager.commit(transactionStatus);
                }
            }
            return obj;
        } catch (Throwable throwable) {
            while (!dtmStack.isEmpty()) {
                TransactionStatus transactionStatus = tsStack.pop();
                PlatformTransactionManager transactionManager = dtmStack.pop();
                if (!transactionStatus.isCompleted()) {
                    transactionManager.rollback(transactionStatus);
                }
            }
            throw new BusinessException(ErrorCode.OPERATION_ERROR, throwable.getMessage());
        }
    }
}
